#pragma once
#include <type_traits>

/**
 * - ADAS: Advanced Driver-Assistance Systems
 * - ACC: Adaptive Cruise Control
 * - (F)CW: (Forward) Collision Warning
 * - PCW: Pedestrian Collision Warning
 * - LDW: Lane Departure Warning
 * - ALC: Auto Lane Change (Assist)
 * - DCLC: Driver Controlled Lane Change
 * - AEB: Automatic Emergency Braking
 * - (A)CAS: (Automated) Collision Avoidance System
 * - NOA: Navigate On Autopilot
 * -----------------------------------------
 * - FD: Follow Distance
 * - TG: Time Gap
*/

enum class HDir { LEFT, RIGHT };
enum class Switch { ON, OFF };

enum class EntityType { MODEL, LINE, MARKER };

enum class ModelType { INVALID, Zhiche, Car, Bus, Truck, Moto, Bike, Cone, Person, Barrier, Tricycle, Undefined, Unknown,
					   Car_L, Car_R, Bus_L, Bus_R, Truck_L, Truck_R, Moto_L, Moto_R, Bike_L, Bike_R, Person_L, Person_R, Tricycle_L, Tricycle_R };
static const std::map<ModelType, const char*> ModelTypeNames = {
	{ModelType::Zhiche, "Zhiche"},
	{ModelType::Car, "Car"},
	{ModelType::Bus, "Bus"},
	{ModelType::Truck, "Truck"},
	{ModelType::Moto, "Moto"},
	{ModelType::Bike, "Bike"},
	{ModelType::Cone, "Cone"},
	{ModelType::Person, "Person"},
	{ModelType::Barrier, "Barrier"},
	{ModelType::Tricycle, "Tricycle"},
	{ModelType::Undefined, "Undefined"},
	{ModelType::Unknown, "Unknown"},
	{ModelType::Car_L, "Car_L"},
	{ModelType::Car_R, "Car_R"},
	{ModelType::Bus_L, "Bus_L"},
	{ModelType::Bus_R, "Bus_R"},
	{ModelType::Truck_L, "Truck_L"},
	{ModelType::Truck_R, "Truck_R"},
	{ModelType::Moto_L, "Moto_L"},
	{ModelType::Moto_R, "Moto_R"},
	{ModelType::Bike_L, "Bike_L"},
	{ModelType::Bike_R, "Bike_R"},
	{ModelType::Person_L, "Person_L"},
	{ModelType::Person_R, "Person_R"},
	{ModelType::Tricycle_L, "Tricycle_L"},
	{ModelType::Tricycle_R, "Tricycle_R"}
};
static const char* Enum_toStr(ModelType type) {
	auto it = ModelTypeNames.find(type);
	if (it != ModelTypeNames.end())
		return it->second;
	throw std::runtime_error("Invalid ModelType: no corresponding string found.");
}

/** from left to right: L9 L8 L7 L6 L5 L4 L3 L2 L [Main Road] R R2 R3 R4 R5 R6 R7 R8 R9 */
enum class LineTag { L9, L8, L7, L6, L5, L4, L3, L2, L, R, R2, R3, R4, R5, R6, R7, R8, R9 };

enum class RenderOrder { INVALID, Opaque, Second, Third, Fourth, Fifth, Sixth, Seventh, ZhicheAsLast, AfterZhiche };

enum class ShaderType { VertexShader, FragmentShader, GeometryShader, ComputeShader };

enum class PrimitiveType { TRIANGLES, TRIANGLE_STRIP, TRIANGLE_FAN, POINTS };

enum class GLSLVarName { Vertex, Normal, Texcoord, InstancedVertex, VertexY, InstancedVertexY, M, V, P, MV, MVP, MVInvTr };
static const std::map<GLSLVarName, const char*> GLSLVarNames = {
	{GLSLVarName::Vertex, "iVertex"},
	{GLSLVarName::Normal, "iNormal"},
	{GLSLVarName::Texcoord, "iTexcoord"},
	{GLSLVarName::InstancedVertex, "iInstancedVertex"},
	{GLSLVarName::VertexY, "uY"},
	{GLSLVarName::InstancedVertexY, "uInstancedY"},
	{GLSLVarName::M, "uM"},
	{GLSLVarName::V, "uV"},
	{GLSLVarName::P, "uP"},
	{GLSLVarName::MV, "uMV"},
	{GLSLVarName::MVP, "uMVP"},
	{GLSLVarName::MVInvTr, "uMVInvTr"}
};
static const char* Enum_toStr(GLSLVarName varName) {
	auto it = GLSLVarNames.find(varName);
	if (it != GLSLVarNames.end())
		return it->second;
	throw std::runtime_error("Invalid GLSLVarName: no corresponding string found.");
}

enum class LineStyle { INVALID, Solid, Dashed, Dashed_Solid, Solid_Dashed, Double_Dashed, Double_Solid, Deceleration_Solid, Deceleration_Dashed, RoadEdge };

enum class RoadBgStyle { DotMatrix, Fill };

enum class RoadEdgeStyle { Dashed, Continuous };

/**@note Invalid: nothing/invalid status value, Off: make the on-going satus off/closed proactively */

enum class ALC { INVALID, Left_Normal, Left_Warning, Right_Normal, Right_Warning, Off };
static bool Enum_leftExists(ALC alc) { return alc == ALC::Left_Normal || alc == ALC::Left_Warning;  }
static bool Enum_rightExists(ALC alc) { return alc == ALC::Right_Normal || alc == ALC::Right_Warning; }

enum class LDW { INVALID, From_Left_Warning, From_Right_Warning, Off };

enum class DCLC { INVALID, ToLeft, ToRight, Mid, Off };

enum class ACC_TG { INVALID, Gear_First, Gear_Second, Gear_Third, Gear_Fourth, Off };

enum class TailLamp { INVALID, Turn_Left, Turn_Right, Turn_Both, Brake_On, Off };// because Turn and Brake share the same tail lamps

static bool Enum_isInvalidOrOff(ALC alc) { return alc == ALC::INVALID || alc == ALC::Off; }
static bool Enum_isInvalidOrOff(LDW ldw) { return ldw == LDW::INVALID || ldw == LDW::Off; }
static bool Enum_isInvalidOrOff(DCLC dclc) { return dclc == DCLC::INVALID || dclc == DCLC::Off; }
static bool Enum_isInvalidOrOff(ACC_TG accTG) { return accTG == ACC_TG::INVALID || accTG == ACC_TG::Off; }
static bool Enum_isInvalidOrOff(TailLamp tailLamp) { return tailLamp == TailLamp::INVALID || tailLamp == TailLamp::Off; }

enum class MoveStatus { INVALID, Moving, Slow, Still, Stopped };

/******************* SQ-Android *******************/
namespace sq {
	enum class ACC { INVALID, Follow, Off };
	enum class FCW { INVALID, Red, Yellow, Off };
	enum class ALC { INVALID, Left, Right, Off };
	enum class ACAS { INVALID, Left, Right, Off };
	enum class NOA { INVALID, On, Off };

	enum TARGET_RANGE { ALC_H_RADIUS = 4, ALC_V_RADIUS = 15, ACAS_H_RADIUS = 4, ACAS_V_RADIUS = 5 };
}

static bool Enum_isInvalidOrOff(sq::ACC acc) { return acc == sq::ACC::INVALID || acc == sq::ACC::Off; }
static bool Enum_isInvalidOrOff(sq::FCW fcw) { return fcw == sq::FCW::INVALID || fcw == sq::FCW::Off; }
static bool Enum_isInvalidOrOff(sq::ALC alc) { return alc == sq::ALC::INVALID || alc == sq::ALC::Off; }
static bool Enum_isInvalidOrOff(sq::ACAS acas) { return acas == sq::ACAS::INVALID || acas == sq::ACAS::Off; }
static bool Enum_isInvalidOrOff(sq::NOA noa) { return noa == sq::NOA::INVALID || noa == sq::NOA::Off; }